package com.mazaiting.instructions;

import com.mazaiting.instructions.base.Instruction;
import com.mazaiting.instructions.base.NoOperandsInstruction;
import com.mazaiting.instructions.comparisons.*;
import com.mazaiting.instructions.constants.ConstInstruction;
import com.mazaiting.instructions.constants.IPushInstruction;
import com.mazaiting.instructions.constants.LdcInstruction;
import com.mazaiting.instructions.control.GotoInstruction;
import com.mazaiting.instructions.control.LookupSwitchInstruction;
import com.mazaiting.instructions.control.ReturnInstruction;
import com.mazaiting.instructions.control.TablesSwitchInstruction;
import com.mazaiting.instructions.conversions.D2xInstruction;
import com.mazaiting.instructions.conversions.F2xInstruction;
import com.mazaiting.instructions.conversions.I2xInstruction;
import com.mazaiting.instructions.conversions.L2xInstruction;
import com.mazaiting.instructions.extended.GotoWInstruction;
import com.mazaiting.instructions.extended.IfNullInstruction;
import com.mazaiting.instructions.extended.WideInstruction;
import com.mazaiting.instructions.loads.*;
import com.mazaiting.instructions.math.*;
import com.mazaiting.instructions.references.*;
import com.mazaiting.instructions.stack.DupInstruction;
import com.mazaiting.instructions.stack.PopInstruction;
import com.mazaiting.instructions.stack.SwapInstruction;
import com.mazaiting.instructions.stores.*;
import com.mazaiting.util.ByteUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 指令工厂
 *
 * @author mazaiting
 * @since 2021/8/15
 */
@Slf4j
public class InstructionFactory {
    private static final Instruction NOP = new NoOperandsInstruction();

    private static final Instruction A_CONST_NULL = new ConstInstruction.AConstNull();
    private static final Instruction I_CONST_M1 = new ConstInstruction.IConstM1();
    private static final Instruction I_CONST_0 = new ConstInstruction.IConst0();
    private static final Instruction I_CONST_1 = new ConstInstruction.IConst1();
    private static final Instruction I_CONST_2 = new ConstInstruction.IConst2();
    private static final Instruction I_CONST_3 = new ConstInstruction.IConst3();
    private static final Instruction I_CONST_4 = new ConstInstruction.IConst4();
    private static final Instruction I_CONST_5 = new ConstInstruction.IConst5();
    private static final Instruction L_CONST_0 = new ConstInstruction.LConst0();
    private static final Instruction L_CONST_1 = new ConstInstruction.LConst1();
    private static final Instruction F_CONST_0 = new ConstInstruction.FConst0();
    private static final Instruction F_CONST_1 = new ConstInstruction.FConst1();
    private static final Instruction F_CONST_2 = new ConstInstruction.FConst2();
    private static final Instruction D_CONST_0 = new ConstInstruction.DConst0();
    private static final Instruction D_CONST_1 = new ConstInstruction.DConst1();

    private static final Instruction I_LOAD_0 = new ILoadInstruction.ILoad0();
    private static final Instruction I_LOAD_1 = new ILoadInstruction.ILoad1();
    private static final Instruction I_LOAD_2 = new ILoadInstruction.ILoad2();
    private static final Instruction I_LOAD_3 = new ILoadInstruction.ILoad3();
    private static final Instruction L_LOAD_0 = new LLoadInstruction.LLoad0();
    private static final Instruction L_LOAD_1 = new LLoadInstruction.LLoad1();
    private static final Instruction L_LOAD_2 = new LLoadInstruction.LLoad2();
    private static final Instruction L_LOAD_3 = new LLoadInstruction.LLoad3();
    private static final Instruction F_LOAD_0 = new FLoadInstruction.FLoad0();
    private static final Instruction F_LOAD_1 = new FLoadInstruction.FLoad1();
    private static final Instruction F_LOAD_2 = new FLoadInstruction.FLoad2();
    private static final Instruction F_LOAD_3 = new FLoadInstruction.FLoad3();
    private static final Instruction D_LOAD_0 = new DLoadInstruction.DLoad0();
    private static final Instruction D_LOAD_1 = new DLoadInstruction.DLoad1();
    private static final Instruction D_LOAD_2 = new DLoadInstruction.DLoad2();
    private static final Instruction D_LOAD_3 = new DLoadInstruction.DLoad3();
    private static final Instruction A_LOAD_0 = new ALoadInstruction.ALoad0();
    private static final Instruction A_LOAD_1 = new ALoadInstruction.ALoad1();
    private static final Instruction A_LOAD_2 = new ALoadInstruction.ALoad2();
    private static final Instruction A_LOAD_3 = new ALoadInstruction.ALoad3();

    private static final Instruction I_STORE_0 = new IStoreInstruction.IStore0();
    private static final Instruction I_STORE_1 = new IStoreInstruction.IStore1();
    private static final Instruction I_STORE_2 = new IStoreInstruction.IStore2();
    private static final Instruction I_STORE_3 = new IStoreInstruction.IStore3();
    private static final Instruction L_STORE_0 = new LStoreInstruction.LStore0();
    private static final Instruction L_STORE_1 = new LStoreInstruction.LStore1();
    private static final Instruction L_STORE_2 = new LStoreInstruction.LStore2();
    private static final Instruction L_STORE_3 = new LStoreInstruction.LStore3();
    private static final Instruction F_STORE_0 = new FStoreInstruction.FStore0();
    private static final Instruction F_STORE_1 = new FStoreInstruction.FStore1();
    private static final Instruction F_STORE_2 = new FStoreInstruction.FStore2();
    private static final Instruction F_STORE_3 = new FStoreInstruction.FStore3();
    private static final Instruction D_STORE_0 = new DStoreInstruction.DStore0();
    private static final Instruction D_STORE_1 = new DStoreInstruction.DStore1();
    private static final Instruction D_STORE_2 = new DStoreInstruction.DStore2();
    private static final Instruction D_STORE_3 = new DStoreInstruction.DStore3();
    private static final Instruction A_STORE_0 = new AStoreInstruction.AStore0();
    private static final Instruction A_STORE_1 = new AStoreInstruction.AStore1();
    private static final Instruction A_STORE_2 = new AStoreInstruction.AStore2();
    private static final Instruction A_STORE_3 = new AStoreInstruction.AStore3();

    private static final Instruction POP = new PopInstruction.Pop();
    private static final Instruction POP2 = new PopInstruction.Pop2();
    private static final Instruction DUP = new DupInstruction.Dup();
    private static final Instruction DUP_X1 = new DupInstruction.DupX1();
    private static final Instruction DUP_X2 = new DupInstruction.DupX2();
    private static final Instruction DUP2 = new DupInstruction.Dup2();
    private static final Instruction DUP2_X1 = new DupInstruction.Dup2X1();
    private static final Instruction DUP2_X2 = new DupInstruction.Dup2X2();
    private static final Instruction SWAP = new SwapInstruction.Swap();

    private static final Instruction I_ADD = new AddInstruction.IAdd();
    private static final Instruction L_ADD = new AddInstruction.LAdd();
    private static final Instruction F_ADD = new AddInstruction.FAdd();
    private static final Instruction D_ADD = new AddInstruction.DAdd();
    private static final Instruction I_SUB = new SubInstruction.ISub();
    private static final Instruction L_SUB = new SubInstruction.LSub();
    private static final Instruction F_SUB = new SubInstruction.FSub();
    private static final Instruction D_SUB = new SubInstruction.DSub();
    private static final Instruction I_MUL = new MulInstruction.IMul();
    private static final Instruction L_MUL = new MulInstruction.LMul();
    private static final Instruction F_MUL = new MulInstruction.FMul();
    private static final Instruction D_MUL = new MulInstruction.DMul();
    private static final Instruction I_DIV = new DivInstruction.IDiv();
    private static final Instruction L_DIV = new DivInstruction.LDiv();
    private static final Instruction F_DIV = new DivInstruction.FDiv();
    private static final Instruction D_DIV = new DivInstruction.DDiv();
    private static final Instruction I_REM = new RemInstruction.IRem();
    private static final Instruction L_REM = new RemInstruction.LRem();
    private static final Instruction F_REM = new RemInstruction.FRem();
    private static final Instruction D_REM = new RemInstruction.DRem();
    private static final Instruction I_NEG = new NegInstruction.INeg();
    private static final Instruction L_NEG = new NegInstruction.LNeg();
    private static final Instruction F_NEG = new NegInstruction.FNeg();
    private static final Instruction D_NEG = new NegInstruction.DNeg();

    private static final Instruction I_SHL = new ShInstruction.IShl();
    private static final Instruction L_SHL = new ShInstruction.LShl();
    private static final Instruction I_SHR = new ShInstruction.IShr();
    private static final Instruction L_SHR = new ShInstruction.LShr();
    private static final Instruction IU_SHR = new ShInstruction.IUShr();
    private static final Instruction LU_SHR = new ShInstruction.LUShr();

    private static final Instruction I_AND = new AndInstruction.IAnd();
    private static final Instruction L_AND = new AndInstruction.LAnd();
    private static final Instruction I_OR = new OrInstruction.IOr();
    private static final Instruction L_OR = new OrInstruction.LOr();
    private static final Instruction I_XOR = new XorInstruction.IXor();
    private static final Instruction L_XOR = new XorInstruction.LXor();

    private static final Instruction I2L = new I2xInstruction.I2L();
    private static final Instruction I2F = new I2xInstruction.I2F();
    private static final Instruction I2D = new I2xInstruction.I2D();
    private static final Instruction L2I = new L2xInstruction.L2I();
    private static final Instruction L2F = new L2xInstruction.L2F();
    private static final Instruction L2D = new L2xInstruction.L2D();
    private static final Instruction F2I = new F2xInstruction.F2I();
    private static final Instruction F2L = new F2xInstruction.F2L();
    private static final Instruction F2D = new F2xInstruction.F2D();
    private static final Instruction D2I = new D2xInstruction.D2I();
    private static final Instruction D2L = new D2xInstruction.D2L();
    private static final Instruction D2F = new D2xInstruction.D2F();
    private static final Instruction I2B = new I2xInstruction.I2B();
    private static final Instruction I2C = new I2xInstruction.I2C();
    private static final Instruction I2S = new I2xInstruction.I2S();

    private static final Instruction L_CMP = new LCmpInstruction.LCmp();
    private static final Instruction F_CMP_L = new FCmpInstruction.FCmpL();
    private static final Instruction F_CMP_G = new FCmpInstruction.FCmpG();
    private static final Instruction D_CMP_L = new DCmpInstruction.DCmpL();
    private static final Instruction D_CMP_G = new DCmpInstruction.DCmpG();

    private static final Instruction I_RETURN = new ReturnInstruction.IReturn();
    private static final Instruction L_RETURN = new ReturnInstruction.LReturn();
    private static final Instruction F_RETURN = new ReturnInstruction.FReturn();
    private static final Instruction D_RETURN = new ReturnInstruction.DReturn();
    private static final Instruction A_RETURN = new ReturnInstruction.AReturn();
    private static final Instruction RETURN = new ReturnInstruction.Return();

    /**
     * 创建新指令
     *
     * @param code 指令代码
     * @return 指令
     */
    public static Instruction newInstruction(short code) {
        //log.debug("opcode: 0x" + ByteUtil.bytes2HexString(ByteUtil.shortToByte(code)));
        switch (code) {
            case 0x00:
                return NOP;
            case 0x01:
                return A_CONST_NULL;
            case 0x02:
                return I_CONST_M1;
            case 0x03:
                return I_CONST_0;
            case 0x04:
                return I_CONST_1;
            case 0x05:
                return I_CONST_2;
            case 0x06:
                return I_CONST_3;
            case 0x07:
                return I_CONST_4;
            case 0x08:
                return I_CONST_5;
            case 0x09:
                return L_CONST_0;
            case 0x0a:
                return L_CONST_1;
            case 0x0b:
                return F_CONST_0;
            case 0x0c:
                return F_CONST_1;
            case 0x0d:
                return F_CONST_2;
            case 0x0e:
                return D_CONST_0;
            case 0x0f:
                return D_CONST_1;
            case 0x10:
                return new IPushInstruction.BiPush();
            case 0x11:
                return new IPushInstruction.SiPush();
            case 0x12:
                return new LdcInstruction.Ldc();
            case 0x13:
                return new LdcInstruction.LdcW();
            case 0x14:
                return new LdcInstruction.Ldc2W();
            case 0x15:
                return new ILoadInstruction.ILoad();
            case 0x16:
                return new LLoadInstruction.LLoad();
            case 0x17:
                return new FLoadInstruction.FLoad();
            case 0x18:
                return new DLoadInstruction.DLoad();
            case 0x19:
                return new ALoadInstruction.ALoad();
            case 0x1a:
                return I_LOAD_0;
            case 0x1b:
                return I_LOAD_1;
            case 0x1c:
                return I_LOAD_2;
            case 0x1d:
                return I_LOAD_3;
            case 0x1e:
                return L_LOAD_0;
            case 0x1f:
                return L_LOAD_1;
            case 0x20:
                return L_LOAD_2;
            case 0x21:
                return L_LOAD_3;
            case 0x22:
                return F_LOAD_0;
            case 0x23:
                return F_LOAD_1;
            case 0x24:
                return F_LOAD_2;
            case 0x25:
                return F_LOAD_3;
            case 0x26:
                return D_LOAD_0;
            case 0x27:
                return D_LOAD_1;
            case 0x28:
                return D_LOAD_2;
            case 0x29:
                return D_LOAD_3;
            case 0x2a:
                return A_LOAD_0;
            case 0x2b:
                return A_LOAD_1;
            case 0x2c:
                return A_LOAD_2;
            case 0x2d:
                return A_LOAD_3;
            case 0x36:
                return new IStoreInstruction.IStore();
            case 0x37:
                return new LStoreInstruction.LStore();
            case 0x38:
                return new FStoreInstruction.FStore();
            case 0x39:
                return new DStoreInstruction.DStore();
            case 0x3a:
                return new AStoreInstruction.AStore();
            case 0x3b:
                return I_STORE_0;
            case 0x3c:
                return I_STORE_1;
            case 0x3d:
                return I_STORE_2;
            case 0x3e:
                return I_STORE_3;
            case 0x3f:
                return L_STORE_0;
            case 0x40:
                return L_STORE_1;
            case 0x41:
                return L_STORE_2;
            case 0x42:
                return L_STORE_3;
            case 0x43:
                return F_STORE_0;
            case 0x44:
                return F_STORE_1;
            case 0x45:
                return F_STORE_2;
            case 0x46:
                return F_STORE_3;
            case 0x47:
                return D_STORE_0;
            case 0x48:
                return D_STORE_1;
            case 0x49:
                return D_STORE_2;
            case 0x4a:
                return D_STORE_3;
            case 0x4b:
                return A_STORE_0;
            case 0x4c:
                return A_STORE_1;
            case 0x4d:
                return A_STORE_2;
            case 0x4e:
                return A_STORE_3;
            case 0x57:
                return POP;
            case 0x58:
                return POP2;
            case 0x59:
                return DUP;
            case 0x5a:
                return DUP_X1;
            case 0x5b:
                return DUP_X2;
            case 0x5c:
                return DUP2;
            case 0x5d:
                return DUP2_X1;
            case 0x5e:
                return DUP2_X2;
            case 0x5f:
                return SWAP;
            case 0x60:
                return I_ADD;
            case 0x61:
                return L_ADD;
            case 0x62:
                return F_ADD;
            case 0x63:
                return D_ADD;
            case 0x64:
                return I_SUB;
            case 0x65:
                return L_SUB;
            case 0x66:
                return F_SUB;
            case 0x67:
                return D_SUB;
            case 0x68:
                return I_MUL;
            case 0x69:
                return L_MUL;
            case 0x6a:
                return F_MUL;
            case 0x6b:
                return D_MUL;
            case 0x6c:
                return I_DIV;
            case 0x6d:
                return L_DIV;
            case 0x6e:
                return F_DIV;
            case 0x6f:
                return D_DIV;
            case 0x70:
                return I_REM;
            case 0x71:
                return L_REM;
            case 0x72:
                return F_REM;
            case 0x73:
                return D_REM;
            case 0x74:
                return I_NEG;
            case 0x75:
                return L_NEG;
            case 0x76:
                return F_NEG;
            case 0x77:
                return D_NEG;
            case 0x78:
                return I_SHL;
            case 0x79:
                return L_SHL;
            case 0x7a:
                return I_SHR;
            case 0x7b:
                return L_SHR;
            case 0x7c:
                return IU_SHR;
            case 0x7d:
                return LU_SHR;
            case 0x7e:
                return I_AND;
            case 0x7f:
                return L_AND;
            case 0x80:
                return I_OR;
            case 0x81:
                return L_OR;
            case 0x82:
                return I_XOR;
            case 0x83:
                return L_XOR;
            case 0x84:
                return new IIncInstruction.IInc();
            case 0x85:
                return I2L;
            case 0x86:
                return I2F;
            case 0x87:
                return I2D;
            case 0x88:
                return L2I;
            case 0x89:
                return L2F;
            case 0x8a:
                return L2D;
            case 0x8b:
                return F2I;
            case 0x8c:
                return F2L;
            case 0x8d:
                return F2D;
            case 0x8e:
                return D2I;
            case 0x8f:
                return D2L;
            case 0x90:
                return D2F;
            case 0x91:
                return I2B;
            case 0x92:
                return I2C;
            case 0x93:
                return I2S;
            case 0x94:
                return L_CMP;
            case 0x95:
                return F_CMP_L;
            case 0x96:
                return F_CMP_G;
            case 0x97:
                return D_CMP_L;
            case 0x98:
                return D_CMP_G;
            case 0x99:
                return new IfCondInstruction.IfEq();
            case 0x9a:
                return new IfCondInstruction.IfNe();
            case 0x9b:
                return new IfCondInstruction.IfLt();
            case 0x9c:
                return new IfCondInstruction.IfGe();
            case 0x9d:
                return new IfCondInstruction.IfGt();
            case 0x9e:
                return new IfCondInstruction.IfLe();
            case 0x9f:
                return new IfIcmpInstruction.IfIcmpEq();
            case 0xa0:
                return new IfIcmpInstruction.IfIcmpNe();
            case 0xa1:
                return new IfIcmpInstruction.IfIcmpLt();
            case 0xa2:
                return new IfIcmpInstruction.IfIcmpGe();
            case 0xa3:
                return new IfIcmpInstruction.IfIcmpGt();
            case 0xa4:
                return new IfIcmpInstruction.IfIcmpLe();
            case 0xa5:
                return new IfACmpInstruction.IfACmpEq();
            case 0xa6:
                return new IfACmpInstruction.IfACmpNe();
            case 0xa7:
                return new GotoInstruction.Goto();
            case 0xaa:
                return new TablesSwitchInstruction.TablesSwitch();
            case 0xab:
                return new LookupSwitchInstruction.LookupSwitch();
            case 0xac:
                return I_RETURN;
            case 0xad:
                return L_RETURN;
            case 0xae:
                return F_RETURN;
            case 0xaf:
                return D_RETURN;
            case 0xb0:
                return A_RETURN;
            case 0xb1:
                return RETURN;
            case 0xb2:
                return new StaticInstruction.GetStatic();
            case 0xb3:
                return new StaticInstruction.PutStatic();
            case 0xb4:
                return new FieldInstruction.GetField();
            case 0xb5:
                return new FieldInstruction.PutField();
            case 0xb6:
                return new InvokeInstruction.InvokeVirtual();
            case 0xb7:
                return new InvokeInstruction.InvokeSpecial();
            case 0xb8:
                return new InvokeInstruction.InvokeStatic();
            case 0xb9:
                return new InvokeInstruction.InvokeInterface();
            case 0xbb:
                return new NewInstruction.New();
            case 0xc0:
                return new InstanceInstruction.CheckCast();
            case 0xc1:
                return new InstanceInstruction.InstanceOf();
            case 0xc4:
                return new WideInstruction.Wide();
            case 0xc6:
                return new IfNullInstruction.IfNull();
            case 0xc7:
                return new IfNullInstruction.IfNonNull();
            case 0xc8:
                return new GotoWInstruction.GotoW();
            default:
                throw new RuntimeException("Unsupported opcode: 0x" + ByteUtil.bytes2HexString(ByteUtil.shortToByte(code)));
        }
    }
}
